#include "NTL/ZZ.h"
#include "struct_pack.hpp"

#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/vector.hpp>

namespace boost_pack {
template <typename T>
std::string serialize(const T &t) {
    std::stringstream ss;
    boost::archive::binary_oarchive out_archive(ss);
    try {
        out_archive << t;
        return ss.str();
    } catch (const boost::archive::archive_exception &e) {
        throw std::runtime_error(std::string(e.what()));
    }
}

template <typename T>
T deserialize(const std::string &s) {
    T res;
    std::istringstream iss(s);
    boost::archive::binary_iarchive in_archive(iss);
    try {
        in_archive >> res;
        return res;
    } catch (const boost::archive::archive_exception &e) {
        throw std::runtime_error(std::string(e.what()));
    }
}
} // namespace boost_pack

namespace boost {
namespace serialization {
// NTL::ZZ
template <class Archive>
inline void
save(Archive &ar, const NTL::ZZ &z, const unsigned int /* file_version */) {
    std::vector<unsigned char> buf(NTL::NumBytes(z));
    NTL::BytesFromZZ(buf.data(), z, static_cast<long>(buf.size()));
    ar &buf;
}

template <class Archive>
inline void
load(Archive &ar, NTL::ZZ &z, const unsigned int /* file_version */) {
    std::vector<unsigned char> buf;
    ar &buf;
    NTL::ZZFromBytes(z, buf.data(), static_cast<long>(buf.size()));
}

} // namespace serialization
} // namespace boost
BOOST_SERIALIZATION_SPLIT_FREE(NTL::ZZ)

int main() {
    NTL::ZZ zz(1000);

    auto buffer1 = boost_pack::serialize(zz);
    auto zz1 = boost_pack::deserialize<NTL::ZZ>(buffer1);

    // ???
    // auto buffer2 = struct_pack::serialize<std::string>(zz);
    // auto zz2 = struct_pack::deserialize<NTL::ZZ>(buffer2);
}
